package edu.scsu.csci.context;

import java.util.Map;
import java.util.WeakHashMap;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import edu.scsu.csci.IInterceptor;
import edu.scsu.csci.annotation.ADomainObject;
import edu.scsu.csci.annotation.AInterceptor;
import edu.scsu.csci.annotation.AScope;
import edu.scsu.csci.annotation.validator.AValidate;
import edu.scsu.csci.core.ActionMethod;
import edu.scsu.csci.core.ActionURL;
import edu.scsu.csci.core.EScope;
import edu.scsu.csci.util.ReflectionUtil;

/**
 * This class provides access to all the components defined in the method annotation.
 * 
 * @author Maqbool Khan
 * @since jdk 1.5
 * @see
 * @viz.diagram ActionContext.tpx
 */
public class ActionContext	{
	
	/**
	 * conatins all the retrieved operation contexts.
	 */
	private static Map<ActionURL, ActionContext> operationContexts = new WeakHashMap<ActionURL, ActionContext>();
		
	/**
	 * Command method.
	 */
	private final ActionMethod actionMethod;
		
	/**
	 * interceptor context.
	 */
	private final IInterceptor interceptor;
		
	/**
	 * domain object class.
	 */
	private final Class<?> domainObjectClazz;
	
	/**
	 * domain object scope.
	 */
	private final EScope domainObjectScope;
	
	/**
	 * <p>	the scope in which the object returned from the action invocation should be set.
	 */
	private final EScope returnedObjectScope;
		
	/**
	 * view implementation.
	 */
	private final ViewContext viewContext;
	
	/**
	 * the buisness object whose operation would be executed.
	 */
	private final Object serviceObject;
	
	/**
	 * servlet request
	 */
	private ServletRequest servletRequest;
		
	/**
	 * servlet response
	 */
	private ServletResponse servletResponse;
	
	/**
	 * the name for the method performing the buisness logic.
	 */
	private final String validationMethod;
	
	private final String domainObjectKey;
	
	private final String returnObjectKey;
	
	/**
	 * Returns a cached operation context. If none exists then creates a new one an 
	 * @param url
	 * @return
	 */
	public static ActionContext getInstance(ActionURL commandURL, ServletRequest request, ServletResponse response)	{
		ActionContext context = (operationContexts.get(commandURL));
		if(context == null){
			context = new ActionContext(commandURL, request, response);
			operationContexts.put(commandURL, context);
		}
		return context;
		
	}
	
	/**
	 * Creats the operation context.
	 * 
	 * @param url
	 */
	private ActionContext(ActionURL commandURL, ServletRequest request, ServletResponse response)	{
		
		this.serviceObject = ReflectionUtil.instantiate(commandURL.getActionName());
		this.actionMethod = ActionMethod.getInstance(this.serviceObject,commandURL.getStratergyName());
		
		ADomainObject domainObjectAnnotation = (ADomainObject)this.actionMethod.getAnnotation(ADomainObject.class);
		this.domainObjectClazz = domainObjectAnnotation != null ? domainObjectAnnotation.clazz() : null;
		this.domainObjectScope = domainObjectAnnotation != null ? domainObjectAnnotation.scope() : null;
		this.domainObjectKey = domainObjectAnnotation != null ? domainObjectAnnotation.key() : "";
		
		AScope scopeAnnotation = (AScope)this.actionMethod.getAnnotation(AScope.class);
		this.returnedObjectScope = scopeAnnotation != null ? scopeAnnotation.scope() : EScope.REQUEST;
		this.returnObjectKey = scopeAnnotation != null ? scopeAnnotation.key() : "";
		
		AInterceptor interceptorAnnotation = (AInterceptor)this.actionMethod.getAnnotation(AInterceptor.class);
		this.interceptor = interceptorAnnotation != null ? IInterceptor.InterceptorFactory.create(interceptorAnnotation.interceptors()) 
									: IInterceptor.InterceptorFactory.create(null);
		
		AValidate validateAnnotation = (AValidate)this.actionMethod.getAnnotation(AValidate.class); 
		this.validationMethod = validateAnnotation != null ? validateAnnotation.method() : null;
		
		this.viewContext = ViewContext.getInstance(request, response, actionMethod, serviceObject);
		
		this.servletRequest = request;
		this.servletResponse = response;
	}
	

	/**
	 * Returns a refrence to the command method asscoiated with this operation.
	 * @return
	 */
	public ActionMethod getActionMethod() {
		return actionMethod;
	}

	/**
	 * Returns domain object clazz asscoiated with this operation.
	 * @return
	 */
	public Class<?> getDomainObjectClazz() {
		return domainObjectClazz;
	}

	/**
	 * Returns the scope of the domain object asscoiated with this operation.
	 * @return
	 */
	public EScope getDomainObjectScope() {
		return domainObjectScope;
	}
	

	/**
	 * Returns an refrence of the interceptorContext asscoiated with this operation.
	 * @return
	 */
	public IInterceptor getInterceptors() {
		return interceptor;
	}
	
	/**
	 * Returns a refrence to the buisness object.
	 * 
	 * @return
	 */
	public Object getServiceObject() {
		return serviceObject;
	}
	
	/**
	 * Returns a refrence to the http servlet request object.
	 * 
	 * @return
	 */
	public ServletRequest getServletRequest() {
		return servletRequest;
	}
	
	/**
	 * Returns a refrence to the http servlet response object.
	 * 
	 * @return
	 */
	public ServletResponse getServletResponse() {
		return servletResponse;
	}

	/**
	 * Returns a refrence to the view context.
	 * 
	 * @return
	 */
	public ViewContext getViewContext() {
		return viewContext;
	}

	
	/**
	 * Returns validationMethod.
	 * @return Returns validationMethod.
	 */
	public String getValidationMethod() {
		return validationMethod;
	}

	
	/**
	 * Returns returnedObjectScope.
	 * @return Returns returnedObjectScope.
	 */
	public EScope getReturnedObjectScope() {
	
		return returnedObjectScope;
	}

	
	/**
	 * Returns domainObjectKey.
	 * @return Returns domainObjectKey.
	 */
	public String getDomainObjectKey() {
	
		return domainObjectKey;
	}

	
	/**
	 * Returns returnObjectKey.
	 * @return Returns returnObjectKey.
	 */
	public String getReturnObjectKey() {
	
		return returnObjectKey;
	}
	
	
}
